# -*- coding: UTF-8 -*-
"""
Tests unitaires du module template.py

Ces tests supposent que les modèles suivent la syntaxe suivante :

FILTER_SEPARATOR = '|'
VARIABLE_ATTRIBUTE_SEPARATOR = '.'
BLOCK_TAG_START = '{%'
BLOCK_TAG_END = '%}'
VARIABLE_TAG_START = '{{'
VARIABLE_TAG_END = '}}'
"""

from django.core import template
import unittest

class RandomSyntaxErrorsCheck(unittest.TestCase):

    def testTagsOnOneLine(self):
        "Les balises s'étendant sur plus d'une ligne ne doivent pas être interprétées"
        c = template.Context({'clé':'valeur'})
        t = template.Template('<h1>{{clé\n}}</h1>')
        expected = '<h1>{{clé\n}}</h1>'
        self.assertEqual(expected, t.render(c))

class PlainTextCheck(unittest.TestCase):

    def testPlainText(self):
        "Le texte brut doit normalement passer à travers les mailles de l'analyseur syntaxique des modèles, sans aucune modification"
        c = template.Context()
        t = template.Template('<h1>Succès</h1>')
        expected = '<h1>Succès</h1>'
        self.assertEqual(expected, t.render(c))

class VariableSubstitutionCheck(unittest.TestCase):

    def testSingleTag(self):
        "Les variables doivent normalement être remplacées par leur valeur dans le contexte actuel"
        c = template.Context({'titre':'Succès'})
        t = template.Template('<h1>{{titre}}</h1>')
        expected = '<h1>Succès</h1>'
        self.assertEqual(expected, t.render(c))

    def testDoubleTag(self):
        "L'emploi de plus d'une variable dans un modèle est autorisé"
        c = template.Context({'premiere_balise':'ça', 'deuxieme_balise':'marche'})
        t = template.Template('<h1>{{premiere_balise}} {{deuxieme_balise}}</h1>')
        expected = '<h1>ça marche</h1>'
        self.assertEqual(expected, t.render(c))

    def testNonexistentVariable(self):
        "On doit normalement échouer de manière silencieuse lorsqu'une variable est introuvable dans le contexte actuel"
        c = template.Context({})
        t = template.Template('<h1>{{variable_inconnue}}</h1>')
        expected = '<h1></h1>'
        self.assertEqual(expected, t.render(c))

    def testVariablesWithSpaces(self):
        "Une balise-variable ne doit normalement pas contenir plus d'un mot"
        t = '<h1>{{balise avec plusieurs mots}}</h1>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testEmptyTag(self):
        "Les balises-variable vides doivent normalement déclencher une exception TemplateSyntaxError"
        t = '{{ }}'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)
        t = '{{              }}'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testIntegerContextValue(self):
        "On doit accepter les entiers comme valeurs d'une variable"
        c = template.Context({'var':55})
        t = template.Template('<h1>{{var}}</h1>')
        expected = '<h1>55</h1>'
        self.assertEqual(expected, t.render(c))

    def textIntegerContextKey(self):
        "On doit accepter les entiers comme noms de variable"
        c = template.Context({55:'var'})
        t = template.Template('<h1>{{55}}</h1>')
        expected = '<h1>var</h1>'
        self.assertEqual(expected, t.render(c))

    def testVariableAttributeAccess1(self):
        "Le point permet à un modèle d'appeler l'attribut d'un objet"
        class AClass: pass
        obj = AClass()
        obj.att = 'valeur-att'
        c = template.Context({'var':obj})
        t = template.Template('<h1>{{ var.att }}</h1>')
        expected = '<h1>valeur-att</h1>'
        self.assertEqual(expected, t.render(c))

    def testVariableAttributeAccess2(self):
        "Le point permet à un modèle d'appeler l'attribut d'un objet (et ce même lorsque getattr est défini)"
        class AClass:
            def __getattr__(self, attr):
                return "valeur-att"
        obj = AClass()
        c = template.Context({'var':obj})
        t = template.Template('<h1>{{ var.att }}</h1>')
        expected = '<h1>valeur-att</h1>'
        self.assertEqual(expected, t.render(c))

    def testVariableAttributeAccessMultiple(self):
        "L'accès à un attribut sur plusieurs niveaux est permis"
        class AClass: pass
        obj = AClass()
        obj.article = AClass()
        obj.article.section = AClass()
        obj.article.section.titre = 'Titre'
        c = template.Context({'obj':obj})
        t = template.Template('<h1>{{ obj.article.section.titre }}</h1>')
        expected = '<h1>Titre</h1>'
        self.assertEqual(expected, t.render(c))

    def testNonexistentVariableAttributeObject(self):
        "On doit normalement échouer de manière silencieuse lorsque l'attribut d'une variable est introuvable"
        class AClass: pass
        obj = AClass()
        obj.att = 'valeur-att'
        c = template.Context({'var':obj})
        t = template.Template('<h1>{{ var.att_inexistant }}</h1>')
        expected = '<h1></h1>'
        self.assertEqual(expected, t.render(c))

    def testIllegalUnderscoreInVariableName(self):
        "On doit normalement déclencher TemplateSyntaxError lorsqu'on essaie d'accéder à une variable commençant par un tiret bas"
        t = '<h1>{{ var._att }}</h1>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)
        t = '<h1>{{ _att }}</h1>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testIllegalCharacterInVariableName(self):
        "On doit normalement déclencher TemplateSyntaxError lorsqu'on essaie d'accéder à une variable contenant un caractère interdit"
        t = '<h1>{{ (beurk }}</h1>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)
        t = '<h1>{{ (beurk.test) }}</h1>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)
        t = '<h1>{{ be(urk.test) }}</h1>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testVariableAttributeDictionary(self):
        "Le point permet à un modèle d'appeler la valeur associée à une clé d'un dictionnaire"
        obj = {'att':'valeur-att'}
        c = template.Context({'var':obj})
        t = template.Template('<h1>{{ var.att }}</h1>')
        expected = '<h1>valeur-att</h1>'
        self.assertEqual(expected, t.render(c))

    def testNonexistentVariableAttributeDictionary(self):
        "On doit normalement échouer de manière silencieuse lorsque la clé d'un dictionnaire est introuvable"
        obj = {'att':'valeur-att'}
        c = template.Context({'var':obj})
        t = template.Template('<h1>{{ var.att_inexistant }}</h1>')
        expected = '<h1></h1>'
        self.assertEqual(expected, t.render(c))

    def testVariableAttributeCallable(self):
        "Le point permet à un modèle d'appeler une méthode simple"
        class AClass:
            def hello(self): return 'salut'
        obj = AClass()
        c = template.Context({'var':obj})
        t = template.Template('<h1>{{ var.hello }}</h1>')
        expected = '<h1>salut</h1>'
        self.assertEqual(expected, t.render(c))

    def testVariableAttributeCallableWrongArguments(self):
        "On doit normalement échouer de manière silencieuse lorsqu'on accède à une méthode qui n'est pas simple"
        class AClass:
            def hello(self, name): return 'salut, %s' % name
        obj = AClass()
        c = template.Context({'var':obj})
        t = template.Template('<h1>{{ var.hello }}</h1>')
        expected = '<h1></h1>'
        self.assertEqual(expected, t.render(c))

class VariableFiltersCheck(unittest.TestCase):

    def setUp(self):
        self.c = template.Context({'var':'Salut monsieur le programmeur'})

    def tearDown(self):
        self.c = None

    def testUpper(self):
        "Le filtre 'upper' convertit une chaîne en majuscules"
        t = template.Template('<h1>{{ var|upper }}</h1>')
        expected = '<h1>SALUT MONSIEUR LE PROGRAMMEUR</h1>'
        self.assertEqual(expected, t.render(self.c))

    def testLower(self):
        "Le filtre 'lower' convertit une chaîne en minuscules"
        t = template.Template('<h1>{{ var|lower }}</h1>')
        expected = '<h1>salut monsieur le programmeur</h1>'
        self.assertEqual(expected, t.render(self.c))

    def testUpperThenLower(self):
        "Les filtres peuvent s'enchaîner (upper|lower)"
        t = template.Template('<h1>{{ var|upper|lower }}</h1>')
        expected = '<h1>salut monsieur le programmeur</h1>'
        self.assertEqual(expected, t.render(self.c))

    def testLowerThenUpper(self):
        "Les filtres peuvent s'enchaîner (lower|upper)"
        t = template.Template('<h1>{{ var|lower|upper }}</h1>')
        expected = '<h1>SALUT MONSIEUR LE PROGRAMMEUR</h1>'
        self.assertEqual(expected, t.render(self.c))

    def testSpaceBetweenVariableAndFilterPipe(self):
        "On doit normalement déclencher TemplateSyntaxError lorsqu'il y a un espace entre la variable et la barre verticale qui permet d'appliquer un filtre"
        t = '<h1>{{ var |lower }}</h1>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testSpaceBetweenFilterPipeAndFilterName1(self):
        "On doit normalement déclencher TemplateSyntaxError lorsqu'il y a un espace après la barre verticale permettant d'appliquer un filtre"
        t = '<h1>{{ var| lower }}</h1>'
        expected = '<h1>Salut monsieur le programmeur</h1>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testNonexistentFilter(self):
        "On doit normalement déclencher TemplateSyntaxError lorsqu'on fait référence à un filtre inexistant"
        t = '<h1>{{ var|filtre_inexistant }}</h1>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testDefaultFilter1(self):
        "On doit normalement ignorer l'argument du filtre 'default' lorsque la variable qui lui est passée existe déjà"
        c = template.Context({'var':'Variable'})
        t = template.Template('<h1>{{ var|default:"Valeur" }}</h1>')
        expected = '<h1>Variable</h1>'
        self.assertEqual(expected, t.render(c))

    def testDefaultFilter2(self):
        "On doit normalement utiliser l'argument du filtre 'default' lorsque la variable qui lui est passée n'existe pas"
        c = template.Context({'var':'Variable'})
        t = template.Template('<h1>{{ var_inexistante|default:"Valeur" }}</h1>')
        expected = '<h1>Valeur</h1>'
        self.assertEqual(expected, t.render(c))

    def testDefaultFilter3(self):
        "On doit normalement utiliser l'argument du filtre 'default' lorsque la variable qui lui est passée n'existe pas (valeur avec espaces)"
        c = template.Context({'var':'Variable'})
        t = template.Template('<h1>{{ var_inexistante|default:"Valeur par défaut" }}</h1>')
        expected = '<h1>Valeur par défaut</h1>'
        self.assertEqual(expected, t.render(c))

    def testDefaultFilter4(self):
        "On doit normalement utiliser l'argument du filtre 'default' lorsque la variable qui lui est passée n'existe pas (valeur échappée)"
        c = template.Context({'var':'Variable'})
        t = template.Template('<h1>{{ var_inexistante|default:"Valeur par défaut \"échappée\"" }}</h1>')
        expected = '<h1>Valeur par défaut "échappée"</h1>'
        self.assertEqual(expected, t.render(c))

    def testDefaultFilter4(self):
        "On doit normalement utiliser l'argument du filtre 'default' lorsque la variable qui lui est passée n'existe pas (valeur avec antislash échappé)"
        c = template.Context({'var':'Variable'})
        t = template.Template('<h1>{{ var_inexistante|default:"Valeur par défaut \\\\ avec slashs" }}</h1>')
        expected = '<h1>Valeur par défaut \\ avec slashs</h1>'
        self.assertEqual(expected, t.render(c))

    def testDefaultFilter4(self):
        "On doit normalement utiliser l'argument du filtre 'default' lorsque la variable qui lui est passée n'existe pas (valeur avec un seul antislash)"
        t = '<h1>{{ var_inexistante|default:"Valeur par défaut \\ avec slash" }}</h1>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testIllegalCharacterInFilterName(self):
        "On doit normalement déclencher TemplateSyntaxError lorsqu'on essaie d'accéder à un filtre contenant un caractère interdit"
        t = '<h1>{{ beurk|(lower) }}</h1>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)
        t = '<h1>{{ beurk|low(er) }}</h1>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

class BlockTagCheck(unittest.TestCase):

    def testNonexistentTag(self):
        "On doit normalement déclencher TemplateSyntaxError lorsqu'on utilise des balises-bloc invalides"
        t = '<h1>{% pas-une-balise %}</h1>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testEmptyTag(self):
        "On doit normalement déclencher TemplateSyntaxError lorsqu'on utilise des balises-bloc vides"
        t = '{% %}'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

class FirstOfCheck(unittest.TestCase):

    def testFirstOfDisplaysFirstIfSet(self):
        "Une balise firstof doit normalement afficher le premier élément qui lui est passé si d'une manière ou d'une autre son évaluation renvoie une valeur vraie"
        c = template.Context({'premier': 'un', 'deuxieme': 'deux'})
        t = template.Template('<h1>{% firstof premier deuxieme %}</h1>')
        expected = '<h1>un</h1>'
        self.assertEqual(expected, t.render(c))

    def testFirstOfDisplaysSecondIfFirstIsFalse(self):
        "Une balise firstof doit normalement afficher le deuxième élément qui lui est passé si son évaluation renvoie une valeur vraie et si le premier élément est faux"
        c = template.Context({'premier': '', 'deuxieme': 'deux'})
        t = template.Template('<h1>{% firstof premier deuxieme %}</h1>')
        expected = '<h1>deux</h1>'
        self.assertEqual(expected, t.render(c))

    def testFirstOfRaisesErrorIfEmpty(self):
        "Une balise firstof doit normalement déclencher une erreur de syntaxe si aucun argument ne lui a été passé"
        t = '{% firstof %}'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testFirstOfDoesNothingIfAllAreFalse(self):
        "Une balise firstof ne doit normalement rien afficher si aucun des deux arguments qui lui ont été passés n'est évalué comme étant vrai"
        c = template.Context({'premier': '', 'deuxieme': False})
        t = template.Template('<h1>{% firstof premier deuxieme troisieme %}</h1>')
        expected = '<h1></h1>'
        self.assertEqual(expected, t.render(c))

    def testFirstOfWorksWithInts(self):
        "Est-ce qu'une balise firstof peut afficher un entier?"
        c = template.Context({'premier': 1, 'deuxieme': False})
        t = template.Template('<h1>{% firstof premier deuxieme %}</h1>')
        expected = '<h1>1</h1>'
        self.assertEqual(expected, t.render(c))

class IfStatementCheck(unittest.TestCase):

    def testSingleIfStatementTrue(self):
        "Une instruction if doit normalement afficher son contenu si l'évaluation du test renvoie une valeur vraie"
        c = template.Context({'test':True})
        t = template.Template('<h1>{% if test %}Oui{% endif %}</h1>')
        expected = '<h1>Oui</h1>'
        self.assertEqual(expected, t.render(c))

    def testSingleIfStatementFalse(self):
        "Une instruction if ne doit normalement pas afficher son contenu si l'évaluation du test renvoie une valeur fausse"
        c = template.Context({'test':False})
        t = template.Template('<h1>{% if test %}On ne devrait pas voir ça{% endif %}</h1>')
        expected = '<h1></h1>'
        self.assertEqual(expected, t.render(c))

    def testNestedIfStatementTrueThenTrue(self):
        "Les instructions if imbriquées doivent normalement fonctionner correctement (cas 1)"
        c = template.Context({'test1':True, 'test2':True})
        t = template.Template('<h1>{% if test1 %} Premier {% if test2 %} Deuxième {% endif %} Premier à nouveau {% endif %}</h1>')
        expected = '<h1> Premier  Deuxième  Premier à nouveau </h1>'
        self.assertEqual(expected, t.render(c))

    def testNestedIfStatementTrueThenFalse(self):
        "Les instructions if imbriquées doivent normalement fonctionner correctement (cas 2)"
        c = template.Context({'test1':True, 'test2':False})
        t = template.Template('<h1>{% if test1 %} Premier {% if test2 %} Deuxième {% endif %} Premier à nouveau {% endif %}</h1>')
        expected = '<h1> Premier  Premier à nouveau </h1>'
        self.assertEqual(expected, t.render(c))

    def testNestedIfStatementFalseThenTrue(self):
        "Les instructions if imbriquées doivent normalement fonctionner correctement (cas 3)"
        c = template.Context({'test1':False, 'test2':True})
        t = template.Template('<h1>{% if test1 %} Premier {% if test2 %} Deuxième {% endif %} Premier à nouveau {% endif %}</h1>')
        expected = '<h1></h1>'
        self.assertEqual(expected, t.render(c))

    def testNestedIfStatementFalseThenFalse(self):
        "Les instructions if imbriquées doivent normalement fonctionner correctement (cas 4)"
        c = template.Context({'test1':False, 'test2':False})
        t = template.Template('<h1>{% if test1 %} Premier {% if test2 %} Deuxième {% endif %} Premier à nouveau {% endif %}</h1>')
        expected = '<h1></h1>'
        self.assertEqual(expected, t.render(c))

    def testElseIfTrue(self):
        "Une instruction else ne doit normalement pas s'exécuter si l'évaluation du test renvoie une valeur vraie"
        c = template.Context({'test':True})
        t = template.Template('<h1>{% if test %}Correct{% else %}Incorrect{% endif %}</h1>')
        expected = '<h1>Correct</h1>'
        self.assertEqual(expected, t.render(c))

    def testElseIfFalse(self):
        "Une instruction else doit normalement s'exécuter si l'évaluation du test renvoie une valeur fausse"
        c = template.Context({'test':False})
        t = template.Template('<h1>{% if test %}Incorrect{% else %}Correct{% endif %}</h1>')
        expected = '<h1>Correct</h1>'
        self.assertEqual(expected, t.render(c))

    def testNonClosedIfTag(self):
        "On doit normalement déclencher TemplateSyntaxError lorsqu'on utilise une balise 'if' non-fermée"
        c = template.Context({'test':True})
        t = '<h1>{% if test %}</h1>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testNonexistentTest(self):
        "On doit normalement échouer de manière silencieuse lorsqu'une instruction if fait référence à un test inexistant"
        c = template.Context({'var':'valeur'})
        t = template.Template('<h1>{% if inexistant %}Salut{% endif %}</h1>')
        expected = '<h1></h1>'
        self.assertEqual(expected, t.render(c))

    def testIfTagNoArgs(self):
        "Les instructions if doivent se voir passer un argument (cas 1)"
        t = '<h1>{% if %}Salut{% endif %}</h1>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testIfTagManyArgs(self):
        "Les instructions if doivent se voir passer un argument (cas 2)"
        t = '<h1>{% if tests multiples %}Salut{% endif %}</h1>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testAttributeAccessInIfNode(self):
        "Une balise if doit normalement chercher la valeur d'un attribut de variable avant de le tester"
        class AClass: pass
        obj = AClass()
        obj.article = AClass()
        obj.article.section = AClass()
        obj.article.section.titre = 'Titre'
        c = template.Context({'obj':obj})
        t = template.Template('<h1>{% if obj.article.section.titre %}Salut{% endif %}</h1>')
        expected = '<h1>Salut</h1>'
        self.assertEqual(expected, t.render(c))
        t = template.Template('<h1>{% if obj.article.section.pas_ici %}Salut{% endif %}</h1>')
        expected = '<h1></h1>'
        self.assertEqual(expected, t.render(c))

    def testIfNot(self):
        "Les instructions if prennent en charge 'not' comme argument facultatif"
        t = template.Template('{% if not a %}Pas a{% endif %}')
        c = template.Context({'a': False})
        expected = 'Pas a'
        self.assertEqual(expected, t.render(c))
        c['a'] = True
        expected = ''
        self.assertEqual(expected, t.render(c))

    def testIfOr(self):
        "Les instructions if prennent en charge 'or'"
        t = template.Template('{% if a or b %}Salut{% endif %}')
        c = template.Context({'a': False, 'b': True})
        expected = 'Salut'
        self.assertEqual(expected, t.render(c))
        c['b'] = False
        expected = ''
        self.assertEqual(expected, t.render(c))

    def testIfOrNot(self):
        "Les instructions if prennent en charge les clauses 'or' combinées avec des 'not' facultatifs"
        t = template.Template('{% if a or not b or c%}Salut{% endif %}')
        c = template.Context({'a': False, 'b': False, 'c': False})
        expected = 'Salut'
        self.assertEqual(expected, t.render(c))
        c['b'] = True
        expected = ''
        self.assertEqual(expected, t.render(c))

class ForLoopCheck(unittest.TestCase):

    def testNormalForLoop(self):
        "Une boucle for doit fonctionner de la manière attendue lorsqu'on lui passe une ou plusieurs valeurs"
        c = template.Context({'morceaux': ('1', '2', '3')})
        t = template.Template('<h1>{% for morceau in morceaux %}{{ morceau }}{% endfor %}</h1>')
        expected = '<h1>123</h1>'
        self.assertEqual(expected, t.render(c))

    def testBlankForLoop(self):
        "Une boucle for doit fonctionner de la manière attendue lorsqu'on lui passe une liste vide"
        c = template.Context({'morceaux': []})
        t = template.Template('<h1>{% for morceau in morceaux %}{{ morceau }}{% endfor %}</h1>')
        expected = '<h1></h1>'
        self.assertEqual(expected, t.render(c))

    def testInvalidForTagFourWords(self):
        "On doit normalement déclencher TemplateSyntaxError lorsqu'une instruction 'for' ne fait pas exactement 4 mots"
        t = '<h1>{% for article %}</h1>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testInvalidForTagThirdWord(self):
        "On doit normalement déclencher TemplateSyntaxError lorsque le troisième mot d'une instruction 'for' n'est pas 'in'"
        t = '<h1>{% for article PASIN beurk %}{% endfor %}</h1>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testNonClosedForTag(self):
        "On doit normalement déclencher TemplateSyntaxError lorsqu'on utilise une balise 'for' non-fermée"
        t = '<h1>{% for i in nombres %}{{ i }}</h1>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testNonexistentVariable1(self):
        "On doit normalement échouer de manière silencieuse lorsqu'une boucle fait référence dans sa définition à une variable inexistante"
        c = template.Context({'var':'valeur'})
        t = template.Template('<h1>{% for i in var_inexistante %}<p>{{ var }}</p>{% endfor %}</h1>')
        expected = '<h1></h1>'
        self.assertEqual(expected, t.render(c))

    def testNonexistentVariable2(self):
        "On doit normalement déclencher TemplateSyntaxError lorsqu'une boucle fait référence dans son corps même à une variable inexistante"
        c = template.Context({'ensemble':('valeur1', 'valeur2')})
        t = template.Template('<h1>{% for i in ensemble %}<p>{{ var_inexistante }}</p>{% endfor %}</h1>')
        expected = '<h1><p></p><p></p></h1>'
        self.assertEqual(expected, t.render(c))

    def testAttributeAccessInForNode(self):
        "Une balise for doit normalement chercher la valeur d'un attribut de variable avant de boucler dessus"
        c = template.Context({'article': {'auteurs':('Simon', 'Adrian')}})
        t = template.Template('<p>{% for i in article.auteurs %}{{ i }}{% endfor %}</p>')
        self.assertEqual('<p>SimonAdrian</p>', t.render(c))
        t = template.Template('<p>{% for i in article.inexistant %}{{ i }}{% endfor %}</p>')
        self.assertEqual('<p></p>', t.render(c))

    def testForLoopFirst(self):
        "La variable 'first' d'une boucle for doit fonctionner de la manière attendue"
        c = template.Context({'morceaux': ('1', '2', '3')})
        t = template.Template('<h1>{% for morceau in morceaux %}{% if forloop.first %}<h2>Premier</h2>{% endif %}{{ morceau }}{% endfor %}</h1>')
        expected = '<h1><h2>Premier</h2>123</h1>'
        self.assertEqual(expected, t.render(c))

    def testForLoopLast(self):
        "La variable 'last' d'une boucle for doit fonctionner de la manière attendue"
        c = template.Context({'morceaux': ('1', '2', '3')})
        t = template.Template('<h1>{% for morceau in morceaux %}{% if forloop.last %}<h2>Dernier</h2>{% endif %}{{ morceau }}{% endfor %}</h1>')
        expected = '<h1>12<h2>Dernier</h2>3</h1>'
        self.assertEqual(expected, t.render(c))

class CycleNodeCheck(unittest.TestCase):

    def testNormalUsage(self):
        "Une balise cycle doit fonctionner de la manière attendue"
        c = template.Context({'ensemble':range(10)})
        t = template.Template('{% for i in set %}{% cycle rouge, vert %}-{{ i }} {% endfor %}')
        expected = 'rouge-0 vert-1 rouge-2 vert-3 rouge-4 vert-5 rouge-6 vert-7 rouge-8 vert-9 '
        self.assertEqual(expected, t.render(c))

    def testNoArguments(self):
        "On doit normalement déclencher TemplateSyntaxError lorsqu'on utilise une balise cycle sans argument"
        t = '{% cycle %}'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testOneArgument(self):
        "On doit normalement déclencher TemplateSyntaxError lorsqu'on utilise une balise cycle avec un seul argument"
        t = '{% cycle salut %}'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testExtraInitialSpaces(self):
        "Les espaces en trop présents autour d'une balise cycle et autour de ses arguments doivent normalement être ignorés"
        c = template.Context({'ensemble':range(5)})
        t = template.Template('{% for i in ensemble %}{% cycle  rouge, vert %}{% endfor %}')
        expected = 'rougevertrougevertrouge'
        self.assertEqual(expected, t.render(c))

class TemplateTagNodeCheck(unittest.TestCase):

    def testNormalUsage(self):
        "Une balise templatetag doit fonctionner de la manière attendue"
        c = template.Context()
        t = template.Template('{% templatetag openblock %}{% templatetag closeblock %}{% templatetag openvariable %}{% templatetag closevariable %}')
        expected = '{%%}{{}}'
        self.assertEqual(expected, t.render(c))

    def testNoArguments(self):
        "On doit normalement déclencher TemplateSyntaxError lorsqu'on utilise une balise templatetag sans argument"
        t = '{% templatetag %}'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testTwoArguments(self):
        "On doit normalement déclencher TemplateSyntaxError lorsqu'on utilise une balise templatetag avec plus d'un argument"
        t = '{% templatetag salut au-revoir %}'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)
        t = '{% templatetag salut au-revoir salut encore %}'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

    def testBadArgument(self):
        "On doit normalement déclencher TemplateSyntaxError lorsqu'on utilise une balise templatetag avec un argument invalide"
        t = '{% templatetag salut %}'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

class PluginFilterCheck(unittest.TestCase):

    def custom_filter(self, value, arg):
        "Filtre temporaire qui sert à vérifier si le système de plug-ins pour les filtres fonctionne correctement"
        return "_%s_%s_" % (value, arg)

    def testPluginFilter(self):
        "La prise en charge des plug-ins autorise l'emploi de filtres personnalisés"
        template.register_filter('testunitaire', self.custom_filter, True)
        c = template.Context({'var':'valeur'})
        t = template.Template('<body>{{ var|testunitaire:"salut" }}</body>')
        expected = '<body>_valeur_salut_</body>'
        self.assertEqual(expected, t.render(c))
        template.unregister_filter('testunitaire')

    def testUnregisterPluginFilter(self):
        "La prise en charge des plug-ins autorise la désinscription de filtres personnalisés"
        template.register_filter('testunitaire', self.custom_filter, True)
        c = template.Context({'var':'valeur'})
        t = template.Template('<body>{{ var|testunitaire:"salut" }}</body>')
        rendered = t.render(c) # ne doit pas déclencher d'exception
        template.unregister_filter('testunitaire')

class PluginTagCheck(unittest.TestCase):

    class CustomNode(template.Node):
        "Affiche son argument"
        def __init__(self, arg):
            self.arg = arg

        def render(self, context):
            return '_%s_' % self.arg

    def do_custom_node(self, parser, token):
        "Gère la balise personnalisée 'testunitaire'"
        bits = token.contents.split()
        return self.CustomNode(bits[1])

    def testPluginTag(self):
        "La prise en charge des plug-ins autorise l'emploi de balises personnalisées"
        template.register_tag('testunitaire', self.do_custom_node)
        c = template.Context({})
        t = template.Template('<body>{% testunitaire salut %}</body>')
        expected = '<body>_salut_</body>'
        self.assertEqual(expected, t.render(c))
        template.unregister_tag('testunitaire')

    def testUnregisterPluginTag(self):
        "La prise en charge des plug-ins autorise la désinscription de balises personnalisés"
        template.register_tag('testunitaire', self.do_custom_node)
        c = template.Context({})
        t = template.Template('<body>{% testunitaire salut %}</body>')
        rendered = t.render(c) # ne doit pas déclencher d'exception
        del(t)
        template.unregister_tag('testunitaire')
        t = '<body>{% testunitaire salut %}</body>'
        self.assertRaises(template.TemplateSyntaxError, template.Template, t)

class ContextUsageCheck(unittest.TestCase):

    def testVariableContext2(self):
        "Les variables doivent normalement persister de bloc en bloc"
        c = template.Context({'global':'dehors', 'ensemble': ('1', '2', '3')})
        t = template.Template('<body><h1>{{ global }}</h1>{% for i in ensemble %}<p>{{ i }} {{ global }}</p>{% endfor %}</body>')
        expected = '<body><h1>dehors</h1><p>1 dehors</p><p>2 dehors</p><p>3 dehors</p></body>'
        self.assertEqual(expected, t.render(c))

    def testVariableContext2(self):
        "Les variables définies au sein d'un bloc doivent normalement surcharger les variables de même nom qui se trouvent dans la même portée qu'elles"
        c = template.Context({'i':'dehors', 'ensemble': ('1', '2', '3')})
        t = template.Template('<body><h1>{{ i }}</h1>{% for i in ensemble %}<p>{{ i }}</p>{% endfor %}{{ i }}</body>')
        expected = '<body><h1>dehors</h1><p>1</p><p>2</p><p>3</p>dehors</body>'
        self.assertEqual(expected, t.render(c))

    def testVariableContextDelete(self):
        "Les variables peuvent normalement être supprimées du contexte actuel"
        c = template.Context({'a':'premier', 'b':'deuxième'})
        del c['a']
        self.assertEqual(c.__repr__(), template.Context({'b':'deuxième'}).__repr__())

    def testInvalidVariableContextDelete(self):
        "On doit normalement déclencher KeyError lorsqu'on essaie de supprimer une variable qui n'existe pas dans le contexte actuel"
        c = template.Context({'a':'premier'})
        self.assertRaises(KeyError, c.__delitem__, 'b')

class AdvancedUsageCheck(unittest.TestCase):

    def testIfInsideFor(self):
        "Une instruction if doit normalement s'exécuter de manière répétée lorsqu'elle est incluse au sein d'une instruction for"
        c = template.Context({'ensemble':(True, False, True, True, False)})
        t = template.Template('<ul>{% for i in ensemble %}{% if i %}<li>1</li>{% endif %}{% endfor %}</ul>')
        expected = '<ul><li>1</li><li>1</li><li>1</li></ul>'
        self.assertEqual(expected, t.render(c))

    def testIfElseInsideFor(self):
        "Une instruction if/else doit normalement s'exécuter de manière répétée lorsqu'elle est incluse au sein d'une instruction for"
        c = template.Context({'ensemble':(True, False, True, True, False)})
        t = template.Template('<ul>{% for i in ensemble %}<li>{% if i %}1{% else %}0{% endif %}</li>{% endfor %}</ul>')
        expected = '<ul><li>1</li><li>0</li><li>1</li><li>1</li><li>0</li></ul>'
        self.assertEqual(expected, t.render(c))

    def testForInsideIf_True(self):
        "Une boucle for à l'intérieur d'une instruction if doit normalement s'exécuter si le test est évalué comme étant vrai"
        c = template.Context({'test':True, 'ensemble':('1', '2', '3')})
        t = template.Template('<body>{% if test %}<ul>{% for i in ensemble %}<li>{{ i }}</li>{% endfor %}</ul>{% endif %}</body>')
        expected = '<body><ul><li>1</li><li>2</li><li>3</li></ul></body>'
        self.assertEqual(expected, t.render(c))

    def testForInsideIf_False(self):
        "Une boucle for à l'intérieur d'une instruction if ne doit normalement pas s'exécuter si le test est évalué comme étant faux"
        c = template.Context({'test':False, 'ensemble':('1', '2', '3')})
        t = template.Template('<body>{% if test %}<ul>{% for i in ensemble %}<li>{{ i }}</li>{% endfor %}</ul>{% endif %}</body>')
        expected = '<body></body>'
        self.assertEqual(expected, t.render(c))

    def testForInsideIfInsideFor(self):
        "Une boucle for à l'intérieur d'une instruction if elle-même à l'intérieur d'une boucle for doit fonctionner correctement"
        c = template.Context({'ensemble1': (True, False, False, False, True), 'ensemble2': ('1', '2', '3')})
        t = template.Template('<body>{% for i in ensemble1 %}{% if i %}{% for j in ensemble2 %}{{ j }}{% endfor %}{% endif %}{% endfor %}</body>')
        expected = '<body>123123</body>'
        self.assertEqual(expected, t.render(c))

    def testMultipleRendersWhenCompiled(self):
        "On peut normalement générer le rendu d'un modèle à l'aide de plusieurs contextes différents sans devoir recompiler le modèle"
        t = template.Template('<body>{% for i in ensemble1 %}{% if i %}{% for j in ensemble2 %}{{ j }}{% endfor %}{% endif %}{% endfor %}</body>')
        c = template.Context({'ensemble1': (True, False, False, False, False), 'ensemble2': ('1', '2', '3')})
        self.assertEqual('<body>123</body>', t.render(c))
        c = template.Context({'ensemble1': (True, True, False, False, False), 'ensemble2': ('1', '2', '3')})
        self.assertEqual('<body>123123</body>', t.render(c))
        c = template.Context({'ensemble1': (True, True, True, False, False), 'ensemble2': ('1', '2', '3')})
        self.assertEqual('<body>123123123</body>', t.render(c))
        c = template.Context({'ensemble1': (True, True, True, True, False), 'ensemble2': ('1', '2', '3')})
        self.assertEqual('<body>123123123123</body>', t.render(c))
        c = template.Context({'ensemble1': (True, True, True, True, True), 'ensemble2': ('1', '2', '3')})
        self.assertEqual('<body>123123123123123</body>', t.render(c))

def tests():
    s = unittest.TestLoader().loadTestsFromName(__name__)
    unittest.TextTestRunner(verbosity=0).run(s)

if __name__ == "__main__":
    tests()
